module keyexp(clk,rst,keysel,rndkren,rconsel,loadkey,key,sboxinsel,deckeywen,rndkrout);

output[127:0] rndkrout;
input clk,rndkren,sboxinsel,loadkey,deckeywen;
input[3:0] rconsel;
input[1:0] keysel;
input[31:0] key;
input rst;

wire [127:0] rndkey,enckey,deckey;//轮密钥，初始加密密钥，初始解密密钥
wire [31:0] w1,w2,w3,w4,w5,w6,w7,subword,sboxin;
reg [7:0] rconout;//轮常量输出

//write 8 key bits in the register per cycle by shift right
//1.外部输入的32位密钥通过寄存器，转变为128位
shfreg_128 shfreg_128(clk,rst,loadkey,key,enckey);

//4选1选通器用于从4个来源中选择1个写入轮密钥寄存器作为下一个周期要使用的轮密钥
//2.初始加密/解密阶段，选择enckey/deckey进行密钥扩展；轮加密/轮解密阶段，选择
//{w4,w5,w6,w7}/{w4,w1,w2,w3}进行密钥扩展
mux41_128 mux41_128(keysel,enckey,deckey,{w4,w5,w6,w7},{w4,w1,w2,w3},rndkey); 
//3.将扩展后的轮密钥输出
reg_128_rst rndkreg(clk,rst,rndkren,rndkey,rndkrout);//rndkren有效时，输出要用的轮密钥

//2选1选通器用于从2个来源中选择1个作为S盒变换的输入，加密时选w3进行变换,
//解密时为轮密钥的低32位
assign sboxin=(sboxinsel==0) ? rndkrout[31:0]:w3;
sbox_mux sbox0(sboxin[23:16],subword[31:24]);//字循环和字节代换一起完成
sbox_mux sbox1(sboxin[15:8],subword[23:16]);
sbox_mux sbox2(sboxin[7:0],subword[15:8]);
sbox_mux sbox3(sboxin[31:24],subword[7:0]);

always@(rconsel)//轮常量选择
      case(rconsel)
          4'd0: rconout=8'h01;
          4'd1: rconout=8'h02;
          4'd2: rconout=8'h04;
          4'd3: rconout=8'h08;
          4'd4: rconout=8'h10;
          4'd5: rconout=8'h20;
          4'd6: rconout=8'h40;
          4'd7: rconout=8'h80;
          4'd8: rconout=8'h1b;
          4'd9: rconout=8'h36;
          default: rconout=8'hxx;
      endcase

//i是4的倍数时，w3经过字循环和字节代换后的32位subword，
//由于rconout只有高8位变化，subword高8位与rconout异或；
//之后，结果加密时和w0异或，解密时和w4异或，都是密钥的高32位
//w0,w1,w2,w3
//w4,w5,w6,w7     
assign w4[31:24]=subword[31:24]^rconout^rndkrout[127:120];
assign w4[23:0]=subword[23:0]^rndkrout[119:96];//T^w0或者T^w4
assign w5=w4^rndkrout[95:64];//i不是4的倍数时，w(i-1)^w(i-4),w4^w1
assign w6=w5^rndkrout[63:32];//w5^w2
assign w7=w6^rndkrout[31:0];//w6^w3

//子密钥反推，解密时用。w0和w4算法一样
assign w3=rndkrout[63:32]^rndkrout[31:0];//w6^w7
assign w2=rndkrout[95:64]^rndkrout[63:32];//w4^w6
assign w1=rndkrout[127:96]^rndkrout[95:64];//w4^w5

//deckeywen有效时，把加密时的第10轮密钥输出，成为初始解密密钥
reg_128_rst deckeyreg(clk,rst,deckeywen,{w4,w5,w6,w7},deckey);

endmodule